Completed
Push — master ( f2f9ef...8d6f66 )
by Fike
56s queued 19s
created

Validator.js ➔ severityFactory   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
nc 1
dl 0
loc 6
rs 9.4285
c 1
b 0
f 0
nop 2
1
var Objects = require('../Utility').Objects
2
var TriggerType = require('./TriggerType').TriggerType
3
4
var severityFactory = function (id, weight) {
5
  return {
6
    id: id,
7
    weight: weight
8
  }
9
}
10
11
/**
12
 * @enum
13
 * @readonly
14
 */
15
var Severity = {
16
  None: severityFactory('None', 0),
17
  Minor: severityFactory('Minor', 1),
18
  Major: severityFactory('Major', 2),
19
  Fatal: severityFactory('Fatal', 3)
20
}
21
22
/**
23
 * @typedef {object} TViolation
24
 *
25
 * @property {string} message
26
 * @property {Severity} severity
27
 */
28
29
/**
30
 * A simple wrapper for list of violations, grouped by path.
31
 *
32
 * @class
33
 *
34
 * @property {Object.<string, TViolation[]>} violations Violations grouped by
35
 *   path
36
 * @property {Severity} severity
37
 */
38
function ViolationSet () {
39
  var self = this
40
  var violations = {}
41
  var severity = Severity.None
42
43
  function updateSeverity (l) {
44
    severity = l.weight > severity.weight ? l : severity
45
  }
46
47
  /**
48
   * @param {string} path
49
   * @param {string} message
50
   * @param {Severity} severity
51
   * @return {ViolationSet}
52
   */
53
  this.push = function (path, message, severity) {
54
    violations[path] = violations[path] ? violations[path] : []
55
    violations[path].push({message: message, severity: severity})
56
    updateSeverity(severity)
57
    return self
58
  }
59
60
  this.merge = function (other) {
61
    Object.keys(other.violations).forEach(function (path) {
62
      other.violations[path].forEach(function (violation) {
63
        self.push(path, violation.message, violation.severity)
64
      })
65
    })
66
    return self
67
  }
68
69
  Object.defineProperties(this, {
70
    severity: {get: function () { return severity }},
71
    violations: {get: function () { return violations }}
72
  })
73
}
74
75
var Validator = {
76
  Severity: Severity,
77
  ViolationSet: ViolationSet,
78
  /**
79
   * @param {TScenarioInput} input
80
   *
81
   * @return ViolationSet
82
   */
83
  scenario: function (input) {
84
    var violations = new ViolationSet()
85
    var violation
86
    var handlers = ['deserializer', 'onError', 'onTermination']
87
    handlers.forEach(function (handler) {
88
      var path = '$.' + handler
89
      if (!input[handler]) {
90
        var message = handler + ' handler is missing, default will be used'
91
        violations.push(path, message, Severity.Minor)
92
      } else {
93
        violations.merge(Validator.handler(input[handler], path))
94
      }
95
    })
96
    if (!TriggerType.find(input.trigger)) {
97
      violation = 'Scenario trigger type is not set or invalid'
98
      violations.push('$.trigger', violation, Severity.Fatal)
99
    }
100
    var states = input.states
101
    if (!Objects.isObject(states)) {
102
      violations.push('$.states', 'Expected to be an object', Severity.Fatal)
103
      states = {}
104
    }
105
    var terminalStates = 0
106
    var entrypointStates = 0
107
    Object.keys(states).forEach(function (id) {
108
      var state = states[id]
109
      violations.merge(Validator.state(state, '$.states.' + id))
110
      entrypointStates += state && state.entrypoint ? 1 : 0
111
      terminalStates += state && state.terminal ? 1 : 0
112
    })
113
    if (terminalStates === 0) {
114
      violations.push('$.states', 'No terminal state is defined', Severity.Fatal)
115
    }
116
    if (entrypointStates !== 1) {
117
      violation = entrypointStates + ' entrypoint states are defined ' +
118
        '(exactly one expected)'
119
      violations.push('$.states', violation, Severity.Fatal)
120
    }
121
    return violations
122
  },
123
  /**
124
   * @param {TStateInput} state
125
   * @param {string} [path]
126
   *
127
   * @return {ViolationSet}
128
   */
129
  state: function (state, path) {
130
    path = path || '$'
131
    var violations = new ViolationSet()
132
    if (Objects.isFunction(state)) {
133
      return violations
134
    }
135
    if (!Objects.isObject(state)) {
136
      return violations.push(path, 'Is not an object', Severity.Fatal)
137
    }
138
    var handlers = {transition: Severity.Fatal, abort: Severity.Minor}
139
    Object.keys(handlers).forEach(function (key) {
140
      var prop = path + '.' + key
141
      if (!state[key]) {
142
        violations.push(prop, key + ' handler is missing', handlers[key])
143
      } else {
144
        violations.merge(Validator.handler(state[key], prop))
145
      }
146
    })
147
    return violations
148
  },
149
  handler: function (handler, path) {
150
    path = path || '$'
151
    var violations = new ViolationSet()
152
    if (!handler) {
153
      return violations
154
    }
155
    var callable = handler
156
    if (Objects.isObject(handler)) {
157
      violations.merge(Validator.handler(handler.onTimeout, path + '.onTimeout'))
158
      callable = handler.handler
159
    }
160
    if (!Objects.isFunction(callable)) {
161
      var message = 'Neither handler itself or handler.handler is a function'
162
      violations.push(path, message, Severity.Fatal)
163
    }
164
    return violations
165
  }
166
}
167
168
module.exports = {
169
  Validator: Validator
170
}
171